package cn.edu.gdut.deepcode.service.impl;

import cn.edu.gdut.deepcode.annotation.CanvasIdCheckPoint;
import cn.edu.gdut.deepcode.component.CacheComponent;
import cn.edu.gdut.deepcode.component.GlobalIdGenerator;
import cn.edu.gdut.deepcode.component.SimpleGlobalLock;
import cn.edu.gdut.deepcode.constant.CacheConstant;
import cn.edu.gdut.deepcode.context.UserContext;
import cn.edu.gdut.deepcode.domain.dto.CanvasDTO;
import cn.edu.gdut.deepcode.domain.po.Canvas;
import cn.edu.gdut.deepcode.domain.po.Result;
import cn.edu.gdut.deepcode.domain.vo.CanvasVO;
import cn.edu.gdut.deepcode.mapper.CanvasMapper;
import cn.edu.gdut.deepcode.service.ICanvasService;
import cn.edu.gdut.deepcode.service.ICanvasSnapshotService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.ToLongFunction;

import static cn.edu.gdut.deepcode.constant.AbnormalInfoConstant.CI_EI_1;
import static cn.edu.gdut.deepcode.constant.CacheConstant.*;
import static cn.edu.gdut.deepcode.utils.CommonUtils.OBJECT_MAPPER;

/**
 * <p>
 * 画布基础信息表 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-04-11
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class CanvasServiceImpl extends ServiceImpl<CanvasMapper, Canvas> implements ICanvasService {

    private final GlobalIdGenerator globalIdGenerator;


    private final ICanvasSnapshotService canvasSnapshotService;


    private final StringRedisTemplate redisTemplate;

    private final CacheComponent cacheComponent;

    private final SimpleGlobalLock simpleGlobalLock;


    @Override
    public Result<List<CanvasVO>> getAllCanvasInfo() throws JsonProcessingException {
        // 先从缓存中获取数据
        Long userId = UserContext.getUser();
        List<CanvasVO> vos = cacheComponent.getListCache(CanvasVO.class, CANVAS_INFO_LIST_CACHE_KEY_PREFIX + userId, 0L, Long.MAX_VALUE);
        if (CollUtil.isNotEmpty(vos)) {
            if (vos.size() == 1 && vos.get(0).getCanvasId() == null) {
                // 缓存的是空对象,应该给前端返回空集合
                vos = Collections.emptyList();
            }
            return Result.success(vos);
        } else {
            // 缓存中无数据则利用锁让一个线程来向数据库查询数据即可,其余线程返回null即可
            boolean tryLock = simpleGlobalLock.tryLock(
                    CANVAS_INFO_LOCK_KEY_PREFIX + userId,
                    Thread.currentThread().getId(),
                    COMMON_LOCK_EXPIRE_TIME,
                    COMMON_LOCK_EXPIRE_TIME_UNIT);
            if (!tryLock) {
                // 获取锁失败则直接返回null
                return Result.success();
            }
            try {
                // 获取用户画布集合
                List<Canvas> canvasList = lambdaQuery()
                        .eq(Canvas::getUserId, userId)
                        .list();
                List<CanvasVO> result = null;
                if (CollUtil.isNotEmpty(canvasList)) {
                    // 用户画布集合不为空才查询
                    // 根据创建时间降序排序
                    canvasList.sort(Comparator.comparingLong(new ToLongFunction<Canvas>() {
                        @Override
                        public long applyAsLong(Canvas canvas) {
                            // 将LocalDateTime变为对应的long类型毫秒值
                            return canvas.getUpdateTime()
                                    .atZone(ZoneId.systemDefault())
                                    .toInstant().toEpochMilli();
                        }
                    }).reversed());

//                    System.out.println(canvasList.stream().map(canvas -> canvas.getCreateTime()
//                            .atZone(ZoneId.systemDefault())
//                            .toInstant().toEpochMilli()).toList());

                    result = BeanUtil.copyToList(canvasList, CanvasVO.class);
                    // 获取属于当前用户的画布vo集合
                    List<CanvasVO> canvasVOS = result.stream().filter(CanvasVO::getIsOwner).toList();
                    if (CollUtil.isEmpty(canvasVOS)) {
                        // 当前用户没有属于自己的画布
                        return Result.success(result);
                    }
                    // 获取属于当前用户的画布分享码集合
                    List<String> values = redisTemplate.executePipelined(
                                    new SessionCallback<Object>() {
                                        @Override
                                        public Object execute(RedisOperations operations) throws DataAccessException {
                                            // 遍历属于当前用户的画布vo集合,获取画布id,再获取分享码
                                            canvasVOS.forEach(canvas -> operations.opsForValue()
                                                    .get(CANVAS_SHARED_CODE_PREFIX + canvas.getCanvasId()));
                                            return null;
                                        }
                                    }
                            ).stream()
                            .map(e -> e != null ? (String) e : null)// 类型装换
                            .toList();
                    // 遍历属于当前用户画布VO集合,为vo设置分享码、设置分享状态
                    for (int i = 0; i < canvasVOS.size(); i++) {
                        CanvasVO vo = canvasVOS.get(i);
                        if (!vo.getSharing()) {
                            // 分享状态为false,直接跳过
                            continue;
                        }
                        vo.setSharedCode(values.get(i));
                    }
                }
                // 缓存数据到redis,如果result为null或是空集合则缓存一个空对象,防止缓存穿透
                cacheComponent.appendListToListCache(
                        CollUtil.isEmpty(result) ? List.of(new CanvasVO()) : result,
                        CANVAS_INFO_LIST_CACHE_KEY_PREFIX + userId,
                        COMMON_EXPIRE_TIME,
                        COMMON_EXPIRE_TIME_UNIT
                );
                // 返回result
                return Result.success(result);
            } finally {
                simpleGlobalLock.unlock(
                        CANVAS_INFO_LOCK_KEY_PREFIX + userId,
                        Thread.currentThread().getId()
                );
            }

        }
    }

    @Override
    public Result<Long> saveCanvasInfo(CanvasDTO canvasDTO) {
        Canvas canvas = BeanUtil.copyProperties(canvasDTO, Canvas.class);
        // 设置画布所属用户,生成画布id并设置,设置当前用户为画布主人
        Long canvasId = globalIdGenerator.generate(CacheConstant.GLOBAL_ID_CANVAS_KEY);
        Long userId = UserContext.getUser();
        canvas
                .setUserId(userId)
                .setCanvasId(canvasId)
                .setIsOwner(true)
                .setOwnerId(userId);
        // 存储画布信息
        save(canvas);
        // 清空缓存
        clearCache(userId, canvasId);
        return Result.success(canvasId);
    }


    @Override
    @CanvasIdCheckPoint
    public Result<?> updateCanvasInfo(CanvasDTO canvasDTO) {
        // dto->po
        Canvas canvas = BeanUtil.copyProperties(canvasDTO, Canvas.class);
        // 修改更新时间
        canvas.setUpdateTime(LocalDateTime.now());
        // 根据用户id,画布id修改canvas
        Long userId = UserContext.getUser();
        Long canvasId = canvasDTO.getCanvasId();
        lambdaUpdate()
                .eq(Canvas::getUserId, userId)
                .eq(Canvas::getCanvasId, canvasId)
                .set(Canvas::getUpdateTime,LocalDateTime.now())
                .update(canvas);
        // 清空缓存
        clearCache(userId, canvasId);
        return Result.success();
    }

    @Override
    @CanvasIdCheckPoint
    @Transactional
    public Result<?> deleteCanvasInfoById(Long canvasId) {
        // 获取当前用户id
        Long userId = UserContext.getUser();
        // 获取所有画布
        List<Canvas> canvasList = Db.lambdaQuery(Canvas.class)
                .eq(Canvas::getCanvasId, canvasId)
                .list();
        if (CollUtil.isEmpty(canvasList)) {
            // 画布集合为空记录错误信息并返回null
            log.error(CI_EI_1, List.of(userId, canvasId, canvasList));
            return Result.error();
        }
        // 查看当前用户是否为画布主人
        if (userId.equals(canvasList.get(0).getOwnerId())) {
            // 删除画布分享码
            redisTemplate.delete(CANVAS_SHARED_CODE_PREFIX +canvasId);
            // 根据画布id删除画布信息(包括协作者的画布信息)
            lambdaUpdate()
                    .eq(Canvas::getCanvasId, canvasId)
                    .remove();
            // 删除画布快照及其缓存
            canvasSnapshotService.deleteSnapshotById(canvasId);

            // 清空各个用户的画布基础信息缓存
            for (Long id : canvasList.stream().map(Canvas::getUserId).toList()) {
                // 清空缓存
                clearCache(id, canvasId);
            }
        } else {
            // 不是主人则删除协作画布信息
            lambdaUpdate()
                    .eq(Canvas::getUserId, userId)
                    .eq(Canvas::getCanvasId, canvasId)
                    .remove();
            // 清空缓存
            clearCache(userId, canvasId);
        }
        // 返回结果
        return Result.success();
    }

    /**
     * @param map      key为画布主人id,value为协作者用户id集合
     * @param canvasId 画布id
     */
    @Override
    public void deleteCollaboratorCanvasInfos(Map<Long, List<Long>> map, Long canvasId) {
        List<Long> keys = map.keySet().stream().toList();
        Long ownerId = keys.get(0);
        List<Long> ids = map.get(ownerId);
        lambdaUpdate()
                .ne(Canvas::getUserId, ownerId)
                .eq(Canvas::getCanvasId, canvasId)
                .remove();
        for (Long id : ids) {
            clearCache(id, canvasId);
        }
    }


    @Override
    public Result<CanvasVO> getCanvasInfoById(Long canvasId) throws JsonProcessingException {
        // 先从缓存中获取
        Long userId = UserContext.getUser();
        String cacheStr = cacheComponent.getValueCache(CANVAS_INFO_VALUE_CACHE_KEY_PREFIX + userId + canvasId);
        if ("".equals(cacheStr)) {
            // 缓存的是空字符串,则直接返回
            return Result.success();
        }
        if (cacheStr != null) {
            // 缓存中有数据
            CanvasVO cacheObj = OBJECT_MAPPER.readValue(cacheStr, CanvasVO.class);
            return Result.success(cacheObj);
        }
        // 缓存中无数据则获取锁
        boolean tryLock = simpleGlobalLock.tryLock(
                CANVAS_INFO_LOCK_KEY_PREFIX + userId + canvasId,
                Thread.currentThread().getId(),
                COMMON_LOCK_EXPIRE_TIME,
                COMMON_LOCK_EXPIRE_TIME_UNIT
        );
        if (!tryLock) {
            // 获取锁失败则直接返回
            return Result.success();
        }
        try {
            // 根据canvasId获取画布信息
            CanvasVO canvasVO = BeanUtil.copyProperties(lambdaQuery()
                    .eq(Canvas::getUserId, userId)
                    .eq(Canvas::getCanvasId, canvasId)
                    .one(), CanvasVO.class);
            if (BeanUtil.isNotEmpty(canvasVO) && canvasVO.getIsOwner()) {
                // 如果画布信息存在且当前用户为画布主人则设置分享码和分享状态
                canvasVO.setSharedCode(cacheComponent.getValueCache(CANVAS_SHARED_CODE_PREFIX + canvasId));
            }
            // 缓存数据,如果canvasVO为null则缓存空字符串
            cacheComponent.setValueCache(
                    CANVAS_INFO_VALUE_CACHE_KEY_PREFIX + userId + canvasId,
                    canvasVO == null ? "" : OBJECT_MAPPER.writeValueAsString(canvasVO),
                    COMMON_EXPIRE_TIME,
                    COMMON_EXPIRE_TIME_UNIT
            );
            return Result.success(canvasVO);
        } finally {
            simpleGlobalLock.unlock(
                    CANVAS_INFO_LOCK_KEY_PREFIX + userId + canvasId,
                    Thread.currentThread().getId()
            );
        }
    }

    @Override
    @CanvasIdCheckPoint
    public Result<?> updateCanvasTitle(CanvasDTO canvasDTO) {
        Long userId = UserContext.getUser();
        Long canvasId = canvasDTO.getCanvasId();
        String title = canvasDTO.getTitle();
        // 更新画布标题
        lambdaUpdate()
                .eq(Canvas::getUserId, userId)
                .eq(Canvas::getCanvasId, canvasId)
                .set(Canvas::getTitle, title)
                .set(Canvas::getUpdateTime,LocalDateTime.now())
                .update();
        // 清空缓存
        clearCache(userId, canvasId);
        return Result.success();
    }

    @Override
    @CanvasIdCheckPoint
    public Result<?> updateCanvasImg(CanvasDTO canvasDTO) {
        Long userId = UserContext.getUser();
        Long canvasId = canvasDTO.getCanvasId();
        String img = canvasDTO.getImg();
        Double zoom = canvasDTO.getZoom();
        // 更新画布标题
        lambdaUpdate()
                .eq(Canvas::getUserId, userId)
                .eq(Canvas::getCanvasId, canvasId)
                .set(Canvas::getImg, img)
                .set(Canvas::getZoom, zoom)
                .set(Canvas::getUpdateTime,LocalDateTime.now())
                .update();
        // 清空缓存
        clearCache(userId, canvasId);
        return Result.success();
    }


    public void clearCache(Long userId, Long canvasId) {
        redisTemplate.delete(CANVAS_INFO_LIST_CACHE_KEY_PREFIX + userId);
        redisTemplate.delete(CANVAS_INFO_VALUE_CACHE_KEY_PREFIX + userId + canvasId);
    }
}
